﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using Common;
using ConsoleHost.Extensions;
using EFRepository;
using EFRepository.Migrations;
using HibernatingRhinos.Profiler.Appender.EntityFramework;
using Microsoft.Practices.Unity;
using ServerServices;

namespace ConsoleHost
{
    public class ConsoleHostStart
    {
        public static void Main()
        {
            EntityFrameworkProfiler.Initialize();
            var container = Container.Container.Instance.GetInstance<IUnityContainer>();
            var globalData = container.Resolve<DataStorageSettings>();
            container.RegisterInstance(new ManagerRepository(globalData.Address, globalData.Port,
                                                             globalData.InitialCatalog,
                                                             globalData.UserId, globalData.Password),
                                       new ContainerControlledLifetimeManager());
            container.RegisterType<IAuthentificationService, WcfEntryPoint>();
            //запуск обновления структуры базы по изменениям в бизнес-модели
            Database.SetInitializer(new CustomInitializer());
            var cfg = new Configuration();
            cfg.TargetDatabase = new DbConnectionInfo(globalData.ConnectionString,
                                                      "System.Data.SqlClient");
            cfg.AutomaticMigrationsEnabled = true;
            cfg.AutomaticMigrationDataLossAllowed = true;
            var dbMigrator = new DbMigrator(cfg);
            dbMigrator.Update(null);
            //создание хоста
            using (var host = new UnityServiceHost(container, typeof (WcfEntryPoint)))
            {
                //создание настроек конечной точки для обнаружения сервиса
                IPAddress address = AddressHelper.GetLocalIP(Dns.GetHostName());
                //      AddressHelper.PrintActiveHosts();
                var mexBinding = new CustomBinding(MetadataExchangeBindings.CreateMexTcpBinding());
                var tc = mexBinding.Elements.Find<TcpTransportBindingElement>();
                tc.PortSharingEnabled = true;
                tc.MaxBufferSize = 2147483647;
                tc.MaxBufferPoolSize = 2147483647;
                tc.MaxReceivedMessageSize = 2147483647;
                //создание настроек конечных точек сервиса
                var tcpBinding = new NetTcpBinding
                    {
                        PortSharingEnabled = true,
                        MaxBufferSize = 2147483647,
                        MaxBufferPoolSize = 2147483647,
                        MaxReceivedMessageSize = 2147483647,
                        MaxConnections = 100,
                        ReliableSession = {Enabled = true, InactivityTimeout = TimeSpan.FromMinutes(25)},
                        Security =
                            {
                                Mode = SecurityMode.Message,
                                Message =
                                    {
                                        AlgorithmSuite = SecurityAlgorithmSuite.Basic128,
                                        ClientCredentialType = MessageCredentialType.UserName
                                    }
                            },
                        ReaderQuotas =
                            {
                                MaxArrayLength = 2147483647,
                                MaxBytesPerRead = 2147483647,
                                MaxDepth = 2147483647,
                                MaxStringContentLength = 2147483647,
                                MaxNameTableCharCount = 2147483647
                            },
                    };
                tcpBinding.Security.Transport.ProtectionLevel = ProtectionLevel.None;
                //var uploadBinding = new NetTcpBinding
                //    {
                //        PortSharingEnabled = true,
                //        TransferMode = TransferMode.Streamed,
                //        MaxReceivedMessageSize = 20480000,
                //        MaxBufferSize = 65536,
                //        Security = {Mode = SecurityMode.None}
                //    };
                host.Authorization.ServiceAuthorizationManager = new SingleSessionPerUserManager();
                var behavior = new ServiceMetadataBehavior();

                host.Description.Behaviors.Add(behavior);
                var stb = new ServiceThrottlingBehavior
                    {
                        MaxConcurrentCalls = 1000,
                        MaxConcurrentInstances = 1000,
                        MaxConcurrentSessions = 1000,
                    };
                host.Description.Behaviors.Add(stb);
                host.AddServiceEndpoint(typeof (IAuthentificationService), tcpBinding,
                                        new Uri(String.Format("net.tcp://{0}:8997/Logon", address)));
                host.Description.Endpoints[0].Behaviors.Add(new ReaderQuotaExtension());

                host.AddServiceEndpoint(typeof (IManagerService), tcpBinding,
                                        new Uri(String.Format("net.tcp://{0}:8997/Manager", address)));
                //host.AddServiceEndpoint(typeof (IUploadService), uploadBinding,
                //                        new Uri(String.Format("net.tcp://{0}:8997/Upload", address)));
                host.AddServiceEndpoint(typeof (IMetadataExchange), mexBinding,
                                        String.Format("net.tcp://{0}:8997/mex/Logon", address));
                host.Description.Endpoints[2].Behaviors.Add(new ReaderQuotaExtension());
                host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode =
                    UserNamePasswordValidationMode.Custom;
                host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator
                    {
                        Repository = Container.Container.Instance.GetInstance<ManagerRepository>()
                    };

                var store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
                X509Certificate2Collection certCollection = store.Certificates.Find(X509FindType.FindBySubjectName,
                                                                                    "‎offDinner",
                                                                                    true);
                X509Certificate2 cert = null;
                if (certCollection.Count == 0)
                {
                    try
                    {
                        Assembly ass = Assembly.GetExecutingAssembly();
                        Stream stream = ass.GetManifestResourceStream("ConsoleHost.Security.certificate.pfx");
                        var bytes = new byte[stream.Length];
                        stream.Read(bytes, 0, bytes.Length);
                        cert = new X509Certificate2(bytes, "1");
                        store.Open(OpenFlags.ReadWrite);
                        store.Add(cert);
                        store.Close();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("access to TrustedPeople folder is denied");
                        Console.WriteLine(e.InnerException);
                        store.Close();
                    }
                }
                else
                {
                    cert = certCollection[0];
                }
                //
                host.Credentials.ServiceCertificate.Certificate = cert;
                //TODO:remove this in the end for security!!!!
                host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
                    X509CertificateValidationMode.None;
                host.Credentials.ClientCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
                //
                host.Open();
                Console.WriteLine("Host Started...");
                Console.WriteLine("...");
                Console.WriteLine("Hit Enter key to exit");
                Console.ReadLine();
                host.Close();
            }
        }
    }
}